package org.hackystat.ui.sensordatabrowser.client;

import java.util.ArrayList;
import java.util.Iterator;

import org.hackystat.ui.sensordatabrowser.client.common.Event;
import org.hackystat.ui.sensordatabrowser.client.common.EventItemsPage;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.Widget;

/**
 * This class represents one event of sensor data elements in the SDViewer display, an event is
 * quantized to the the sensor data types occurring within one minute.
 * @author David J. Nickles
 */
public class EventTreeItem extends TreeItem implements ClickListener {

  private int count;
  private boolean lazyLoad;
  private Image rpcStatusImage;
  private Session session;
  private boolean memoryOpen;
  private boolean memoryFireEvents;
  private Event event;
  private int oddoreven;
  private EventItemsPage reqPage;
  
  /**
   * Makes a new EventTreeItem representing this Event.
   * @param event representing a grouping of sdt & tool within a minute's span.
   * @param oddoreven if the parent is odd or even for coloring
   * @param session information. 
   */
  public EventTreeItem(Event event, int oddoreven, Session session) {
    this.session = session;
    this.event = event;
    this.reqPage = new EventItemsPage();
    HorizontalPanel row1 = new HorizontalPanel();
    this.rpcStatusImage = new Image(Images.WAIT_IMAGE);
    this.rpcStatusImage.setPixelSize(15, 15);
    this.rpcStatusImage.setVisible(false);
    row1.add(this.rpcStatusImage);
    Label label = new Label(event.getTitle());
    label.setStyleName("gwt-TreeItem");
    row1.add(label);
    this.setWidget(row1);
    this.count = 0;
    this.lazyLoad = true;
    this.oddoreven = oddoreven;
    if (oddoreven % 2 == 0) {
      addStyleName("gwt-TreeItem-outer-odd");
    } 
    else {
      addStyleName("gwt-TreeItem-even");
    }
    addItem(new TreeItem("place-holder"));
  }
  
  /**
   * Gets the event associated with this EventTreeItem.
   * @return the event.
   */
  public Event getEvent() {
    return this.event;
  }
  
  /**
   * Gets the count of new elements.
   * @return the count of new elements.
   */
  public int size() {
    return this.count;
  }

  /**
   * This method sets the TreeItem state.
   * @param open true if the tree branch is to be expanded, false if collapsed.
   * @param fireEvents true if subtrees should be notified.
   */
  public void setState(boolean open, boolean fireEvents) {
    // if has opened before then return and do nothing (you have already loaded the children)
    if (this.lazyLoad && open) {
      this.rpcStatusImage.setVisible(open);
      rpcGetEventItems();
      this.memoryOpen = open;
      this.memoryFireEvents = fireEvents;
    } 
    else {
      super.setState(open, fireEvents);
    }
  }
  
  /**
   * This method sets the row style to odd or even style.
   * @param item to stylize.
   */
  private void setRowStyle(TreeItem item) {
    if ((++this.oddoreven) % 2 == 0) {
      item.addStyleName("gwt-TreeItem-inner-odd");
    } 
    else {
      item.addStyleName("gwt-TreeItem-even");
    }
  }
  
  /**
   * This method updates the page data structure for the next page fetch.
   */
  private void advancePage() {
    this.reqPage.setDataList(null);
    this.reqPage.setPageNumber(this.reqPage.getPageNumber() + 1);
  }
  
  /**
   * Receive the list of sensor data and insert in place of the hrefs.
   * @param page of sensor data.
   */
  public void insert(EventItemsPage page) {
    if (page.getPageNumber() == 0) {
      removeItems();
    }
    else {
      this.removeItem(getChild(this.getChildCount() - 1));
    }
    if (Boolean.TRUE.toString().equals(page.getStatus())) {
      for (int i = 0; i < page.getDataList().size(); ++i) {
        /** @gwt.typeArgs innerList <java.lang.String> */
        ArrayList innerList = (ArrayList)page.getDataList().get(i);
        TreeItem item = new TreeItem((String)innerList.get(0));
        setRowStyle(item);
        Iterator it = innerList.listIterator(1);
        while (it.hasNext()) {
          item.addItem((String)it.next());
        }
        addItem(item);
      }
      this.reqPage = page;
      if (page.isMore()) {
        advancePage();
        Label label = new Label("Get next " + EventItemsPage.PAGESIZE + " items.");
        label.addStyleName(Styles.HYPERLINKLABEL);
        TreeItem item = addItem(label);
        label.addClickListener(this);
        setRowStyle(item);
        --this.oddoreven;
      }
      this.rpcStatusImage.setVisible(false);
      this.lazyLoad = false;
      super.setState(this.memoryOpen, this.memoryFireEvents);
    }
    else {
      TreeItem item = addItem("Error reported: " + page.getStatus());
      setRowStyle(item);
    }
  }

  /**
   * This method performs an asynchronous remote call out to the sensor data browser service
   * to get a list of days with data.
   */ 
  public void rpcGetEventItems() {
    if (session.getUser() != null) {
      RPCServiceFactory.getInstance().getService().getEventItems(session.getSensorbaseHost(), 
        Session.DEFAULT, session.getUser(), session.getPassword(), this.event, this.reqPage,
        new AsyncCallback()
        {
          public void onSuccess(Object result) {
            insert((EventItemsPage)result);
          }

          public void onFailure(Throwable caught) {
            if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
              session.log(RPCServiceFactory.PRCERRORNAME, "Cannot invoke method on servlet.");
            } 
            else {
              session.log(RPCServiceFactory.PRCERRORNAME, caught.toString());
            }
          }
        });
    }
  }

  /**
   * This method responds to clicks on the get more data items link.
   * @param sender the widget clicked.
   */
  public void onClick(Widget sender) {
    this.removeItem(getChild(this.getChildCount() - 1)); // remove the more 'hyperlink'
    HorizontalPanel hp = new HorizontalPanel();
    Image rpcWait = new Image(Images.WAIT_IMAGE);
    rpcWait.setPixelSize(15, 15);
    hp.add(rpcWait);
    Label labelWait = new Label("Fetching next " + EventItemsPage.PAGESIZE + " items.");
    hp.setStyleName(Styles.ACTIVELINKLABEL);
    hp.add(labelWait);
    this.addItem(hp); // add the rpcWaiting panel
    rpcGetEventItems();
  }
}